#pragma once

#include <utility>

#include "../interface/Address.h"
#include "../interface/Socket.h"
#include "../reactor/Reactor.h"
#include "../../utility/singleton/Singleton.h"

namespace network {

    class TcpServer;

    class ClientConnection : public Socket {

    public:

        using recvCallBack = std::function<void(ClientConnection *)>;

        using closeCallBack = std::function<void(ClientConnection *)>;

        friend class TcpServer;

        /**
          * @brief  创建一个客户端连接
          * @note   默认创建socket、连接状态为断开、reactor为默认单例
          */
        ClientConnection() : Socket(Socket::createSocket()), connected_(false),
                             reactor_(singleton::Singleton<Reactor>::getInstance()) {}

        explicit ClientConnection(Reactor *reactor) : ClientConnection(Socket::createSocket(), false, reactor) {}

        explicit ClientConnection(const Socket &socket, bool connected = false,
                                  Reactor *reactor = singleton::Singleton<Reactor>::getInstance()) : ClientConnection(
            socket.getFd(), Address(socket.getIp(), socket.getPort()), connected, reactor) {}

        explicit ClientConnection(int fd, Address address = Address("127.0.0.1", 8080), bool connected = false,
                                  Reactor *reactor = singleton::Singleton<
                                      Reactor>::getInstance()) : Socket(fd, address), connected_(connected),
                                                                 reactor_(reactor) {}

        /**
        * @brief  连接服务端
        * @param  address 服务端地址
        * @retval 是否连接成功
        */
        bool connect(Address address);

        bool connect(const char *ip, unsigned short port) { return connect(Address(ip, port)); }

        bool connect(const std::string &ip, unsigned short port) { return connect(Address(ip, port)); }

        /**
          * @brief  检测连接状态
          * @retval 连接状态
          */
        bool isConnected() const { return connected_; }

        /**
          * @brief  从读缓冲区读取数据
          * @note   将清空读缓冲区
          * @retval 读缓冲区数据
          */
        std::string read();

        /**
          * @brief  向写缓冲区写入数据并异步发送
          * @note   如果当前未连接则抛出std::runtime_error异常
          * @param  data 要发送的数据
          */
        void send(const std::string &data);

        /**
          * @brief  与服务端断开连接
          */
        void disconnect() const;

        /**
          * @brief  设置客户端断开连接时的回调函数
          * @param  cb 回调函数
          */
        void setRecvCallBack(recvCallBack cb) { recv_cb_ = std::move(cb); }

        /**
          * @brief  设置客户端断开连接时的回调函数
          * @param  cb 回调函数
          */
        void setCloseCallBack(closeCallBack cb) { close_cb_ = std::move(cb); }

    private:

        using Socket::createSocket;

        // 是否连接成功
        bool connected_;

        // 读写缓冲区
        std::string read_buffer_, write_buffer_;

        // 客户端收数据回调函数
        recvCallBack recv_cb_;

        // 客户端断开连接回调函数
        closeCallBack close_cb_;

    protected:

        // 客户端读事件处理
        void clientRecv();

        // 客户端关闭连接事件处理
        void clientClose();

        // 客户端写事件处理
        void clientSend();

        // 反应堆指针
        Reactor *reactor_;

    };

}
